#define __LIBRARY__

#include <unistd.h>

inline _syscall0(int, fork);
inline _syscall1(int,setup,void *,BIOS)

int errno;

#include <asm/system.h>

#include <linux/tty.h>
#include <linux/kernel.h>
#include <linux/sched.h>

#include <stdarg.h>

static char printbuf[1024];

int printf(const char* fmt, ...);

extern int vsprintf(char* buf, const char* fmt, va_list args);
void init();
extern void hd_init(void);
extern void mem_init(long start, long end);

#define EXT_MEM_K (*(unsigned short *)0x90002)
#define DRIVE_INFO (*(struct drive_info *)0x90080)
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
#define ORIG_SWAP_DEV (*(unsigned short *)0x901FA)

static long memory_end = 0;
static long buffer_memory_end = 0;
static long main_memory_start = 0;

struct drive_info { char dummy[32]; } drive_info;

void main(void) 
{
    /*
    char* p1 = "hello world!\0";
    short* p = (short*)0xb8000;
    while(*p1) {
        *p++ = 0xf00 | (*p1++);
    }
    */

    //__asm__("int $0x80\n\r"::);

    drive_info = DRIVE_INFO;

    memory_end = (1<<20) + (EXT_MEM_K<<10);
    memory_end &= 0xfffff000;
    if (memory_end > 16*1024*1024)
        memory_end = 16*1024*1024;
    if (memory_end > 12*1024*1024)
        buffer_memory_end = 4*1024*1024;
    else if (memory_end > 6*1024*1024)
        buffer_memory_end = 2*1024*1024;
    else
        buffer_memory_end = 1*1024*1024;

    main_memory_start = buffer_memory_end;
    mem_init(main_memory_start, memory_end);

    trap_init();
    sched_init();
    buffer_init(buffer_memory_end);
    tty_init();

    printk("\nmemory start at 0x%x, end at 0x%x\n", main_memory_start, memory_end);

    hd_init();

    move_to_user_mode();

    if (!fork()) {
        test_b();
        init();
    }

    __asm__("movl $0x0, %edi\n\r"
            "movw $0x18, %ax\n\t"
            "movw %ax, %gs \n\t"
            "movb $0x0c, %ah\n\r"
            "movb $'A', %al\n\r"
            "movw %ax, %gs:(%edi)\n\r");

    for(;;);
}

int printf(const char* fmt, ...) {
    va_list args;
    int i;

    va_start(args, fmt);
    write(1, printbuf, i = vsprintf(printbuf, fmt, args));
    va_end(args);

    return i;
}

void init() {
    setup((void *) &drive_info);
}

